﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Windows8Accelerator.Core.Xaml.Helpers;
using Windows8Accelerator.Core.Xaml.Storage;

namespace Windows8Accelerator.Core.Xaml.Helpers
{
    public abstract class AppHelperBase
    {
        private static AppHelperBase _Instance;
        public static AppHelperBase Instance
        {
            get
            {
                if (_Instance == null)
                    _Instance = AssemblyHelper.CreateInstanceOfAccelerator<AppHelperBase>();
                return _Instance;
            }
        }

        public abstract Task NavigateAsync(Type pageType);
        public abstract Task NavigateAsync(Type pageType, object parameter);
        public abstract ApplicationViewMode ApplicationViewMode { get; }
        public abstract void RunOnUIAsync(Action action);

        public async Task NavigateAsync(string pageTypeName)
        {
            await NavigateAsync(pageTypeName, null);
        }

        public async Task NavigateAsync(string pageTypeName, object parameter)
        {
            ((AppHelper)(AppHelper.Instance)).NavigationParameter = parameter;
            AppHelper.CurrentPage.NavigationService.Navigate(new Uri(pageTypeName, UriKind.Relative));

            //var pageType = await AssemblyHelper.GetTypeAsync(pageTypeName);
            //await NavigateAsync(pageType, parameter);
        }

        public abstract void TryUnsnap();

        public void Initialize()
        {
            InternalInitialize();
        }

        protected abstract void InternalInitialize();

        private const string PAGE_VIEWMODEL_KEY = "__ViewModel__";

        protected ApplicationState _State = new ApplicationState()
        {
            NavigationStackPosition = -1,
            PageTypeNames = new List<string>(),
            Parameters = new List<object>(),
            PageState = new List<Dictionary<string, object>>(),
            Pages = new List<object>(),
            AppState = new Dictionary<string, object>(),
        };
        public Dictionary<string, object> AppState
        {
            get
            {
                return _State.AppState;
            }
        }

        public T GetPageViewModel<T>(object page)
        {
            return (T)GetPageState(page)[PAGE_VIEWMODEL_KEY];
        }

        public T GetOrCreatePageViewModel<T>(object page)
             where T : new()
        {
            var pageState = GetPageState(page);
            if (!pageState.ContainsKey(PAGE_VIEWMODEL_KEY))
                pageState[PAGE_VIEWMODEL_KEY] = new T();
            return (T)pageState[PAGE_VIEWMODEL_KEY];
        }

        public Dictionary<string, object> GetPageState(object page)
        {
            var pageIndex = _State.Pages.IndexOf(page);
            if (pageIndex == -1)
            {
                pageIndex = _State.NavigationStackPosition;
                _State.Pages[pageIndex] = page;
            };
            return _State.PageState[pageIndex];
        }

        public class ApplicationState
        {
            public List<string> PageTypeNames { get; set; }
            public List<object> Parameters { get; set; }
            [IgnoreDataMember]
            public List<object> Pages { get; set; }
            public List<Dictionary<string, object>> PageState { get; set; }
            public Dictionary<string, object> AppState { get; set; }
            public int NavigationStackPosition { get; set; }
        }

        public async Task SaveStateAsync()
        {
            await ObjectPersister.SaveAsync("AppState.bin", StorageLocation.Local, _State, ObjectPersisterFormat.Binary);
        }

        public async Task LoadStateAsync()
        {
            try
            {
                var state = await ObjectPersister.LoadAsync<ApplicationState>("AppState.bin", StorageLocation.Local, ObjectPersisterFormat.Binary);
                _State = state;
                _State.Pages = new List<object>();
                for (int i = 0; i < _State.PageTypeNames.Count; i++)
                {
                    _State.Pages.Add(null);
                }

                var previousNavigationStackPosition = _State.NavigationStackPosition;
                _State.NavigationStackPosition = -1;
                for (int i = 0; i < _State.PageTypeNames.Count; i++)
                {
                    _State.NavigationStackPosition++;
                    var pageType = Type.GetType(_State.PageTypeNames[i], true);
                    var parameter = _State.Parameters[i];
                    await NavigateAsync(pageType, parameter);
                }
                var navigationStackPosition = _State.NavigationStackPosition;
                while (navigationStackPosition > previousNavigationStackPosition)
                {
                    await NavigateBack();
                    navigationStackPosition--;
                }
            }
            catch { }
        }

        public abstract Task NavigateBack();

        public static Uri TypeToUri(Type pageType)
        {
            if (pageType == null)
                return null;
            var a = pageType.FullName.Substring(pageType.Assembly.GetName().Name.Length);
            a = a.Replace(".", "/") + ".xaml";
            return new Uri(a, UriKind.Relative);
        }

        public object GetNavigationParameter()
        {
            if (_State.NavigationStackPosition < _State.Parameters.Count)
                return _State.Parameters[_State.NavigationStackPosition];
            return null;
        }

        protected void Frame_Navigated(string navigationMode, object page, object parameter)
        {
            if (page == null)
                return;

            var state = _State;
            switch (navigationMode)
            {
                case "Back":
                    state.NavigationStackPosition--;
                    break;
                case "Forward":
                    state.NavigationStackPosition++;
                    break;
                case "New":
                    //SettingsContractHelper.InitializeSettingsPane(page);
                    state.NavigationStackPosition++;
                    RemoveDiscardedItems(state.PageTypeNames);
                    RemoveDiscardedItems(state.Parameters);
                    RemoveDiscardedItems(state.Pages);
                    RemoveDiscardedItems(state.PageState);
                    state.PageTypeNames.Add(page.GetType().AssemblyQualifiedName);
                    state.Parameters.Add(parameter);
                    state.Pages.Add(page);
                    state.PageState.Add(new Dictionary<string, object>());
                    break;
                case "Refresh":
                    break;
                default:
                    break;
            }
        }

        private void RemoveDiscardedItems(IList list)
        {
            while (list.Count > _State.NavigationStackPosition)
                list.RemoveAt(list.Count - 1);
        }

        public abstract bool CanNavigateBack { get; }

        public abstract Task ShowErrorMessageAndQuitAsync(string title, string message);

        public abstract Task ShowErrorMessageAndGoBackAsync(string title, string message);
    }
}
